﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace xpath_parser
{

	public enum Axes
	{
		Attribute,
		Child,
		Namespace,
		Parent,
		Self,
	}

	public enum XPathNodeType
	{
		Root = 0,
		Element = 1,
		Attribute = 2,
		Namespace = 3,
		Text = 4,
		SignificantWhitespace = 5,
		Whitespace = 6,
		ProcessingInstruction = 7,
		Comment = 8,
		All = 9,
	}

	public class Expression
	{

		public class ExprRoot : NodeSet
		{
			//public override String ToString() { return ""; }
			//public override object Evaluate( BaseIterator iter )
			//{
			//    XPathNavigator navRoot = iter.Current.Clone();
			//    navRoot.MoveToRoot();
			//    return new SelfIterator( navRoot, iter.NamespaceManager );
			//}

			//public override XPathNodeType EvaluatedNodeType
			//{
			//    get { return XPathNodeType.Root; }
			//}

			//public override bool Peer
			//{
			//    get { return true; }
			//}

			//public override bool Subtree
			//{
			//    get { return false; }
			//}
		}

		public class ExprSLASH2 : NodeSet
		{
			public Expression left;
			public NodeSet right;

			//static NodeTest DescendantOrSelfStar = new NodeTypeTest( Axes.DescendantOrSelf, XPathNodeType.All );

			public ExprSLASH2( Expression left, NodeSet right )
			{
				this.left = left;
				this.right = right;
			}

			//public override Expression Optimize()
			//{
			//    left = left.Optimize();
			//    right = ( NodeSet )right.Optimize();
			//    // Path A//B is equal to
			//    // A/descendant-or-self::node()/child::B, which is
			//    // equivalent to A/descendant::B. Unlike '//', '/'
			//    // could be optimized by SimpleSlashIterator.
			//    NodeTest rnt = right as NodeTest;
			//    if( rnt != null && rnt.Axis.Axis == Axes.Child )
			//    {
			//        NodeNameTest nameTest = rnt as NodeNameTest;
			//        if( nameTest != null )
			//            return new ExprSLASH( left,
			//                new NodeNameTest( nameTest, Axes.Descendant ) );
			//        NodeTypeTest typeTest = rnt as NodeTypeTest;
			//        if( typeTest != null )
			//            return new ExprSLASH( left,
			//                new NodeTypeTest( typeTest, Axes.Descendant ) );
			//    }
			//    return this;
			//}

			//public override String ToString() { return left.ToString() + "//" + right.ToString(); }
			//public override object Evaluate( BaseIterator iter )
			//{
			//    BaseIterator il = left.EvaluateNodeSet( iter );
			//    if( left.Peer && !left.RequireSorting )
			//        il = new SimpleSlashIterator(
			//            il, DescendantOrSelfStar );
			//    else
			//        il = new SlashIterator( il,
			//            DescendantOrSelfStar,
			//            left.RequireSorting );

			//    return new SlashIterator(
			//        il,
			//        right,
			//        DescendantOrSelfStar.RequireSorting || right.RequireSorting
			//    );
			//}

			//public override bool RequireSorting { get { return left.RequireSorting || right.RequireSorting; } }

			//public override XPathNodeType EvaluatedNodeType
			//{
			//    get { return right.EvaluatedNodeType; }
			//}

			//public override bool IsPositional
			//{
			//    get { return left.IsPositional || right.IsPositional; }
			//}

			//public override bool Peer
			//{
			//    get { return false; }
			//}

			//public override bool Subtree
			//{
			//    get
			//    {
			//        NodeSet n = left as NodeSet;
			//        return n != null && n.Subtree && right.Subtree;
			//    }
			//}
		}

		public class ExprSLASH : NodeSet
		{
			public Expression left;
			public NodeSet right;
			public ExprSLASH( Expression left, NodeSet right )
			{
				this.left = left;
				this.right = right;
			}

			//public override Expression Optimize()
			//{
			//    left = left.Optimize();
			//    right = ( NodeSet )right.Optimize();
			//    return this;
			//}

			public override String ToString() { return left.ToString() + "/" + right.ToString(); }
			//public override object Evaluate( BaseIterator iter )
			//{
			//    // Peer and subtree optimization. see
			//    // http://idealliance.org/papers/dx_xmle04/papers/02-03-02/02-03-02.html
			//    BaseIterator iterLeft = left.EvaluateNodeSet( iter );
			//    if( left.Peer && right.Subtree && !RequireSorting )
			//        return new SimpleSlashIterator( iterLeft, right );
			//    return new SlashIterator( iterLeft, right, RequireSorting );
			//}

			//public override bool RequireSorting { get { return left.RequireSorting || right.RequireSorting; } }

			//public override XPathNodeType EvaluatedNodeType
			//{
			//    get { return right.EvaluatedNodeType; }
			//}

			//public override bool IsPositional
			//{
			//    get { return left.IsPositional || right.IsPositional; }
			//}

			//public override bool Peer
			//{
			//    get { return left.Peer && right.Peer; }
			//}

			//public override bool Subtree
			//{
			//    get
			//    {
			//        NodeSet n = left as NodeSet;
			//        return n != null && n.Subtree && right.Subtree;
			//    }
			//}
		}

		//public class NodeTypeTest : NodeTest
		//{
		//    public readonly XPathNodeType type;
		//    protected String _param;
		//    public NodeTypeTest( Axes axis )
		//        : base( axis )
		//    {
		//        this.type = _axis.NodeType;
		//    }
		//    public NodeTypeTest( Axes axis, XPathNodeType type )
		//        : base( axis )
		//    {
		//        this.type = type;
		//    }
		//    // FIXME: Better description
		//    public NodeTypeTest( Axes axis, XPathNodeType type, String param )
		//        : base( axis )
		//    {
		//        this.type = type;
		//        _param = param;
		//        if( param != null && type != XPathNodeType.ProcessingInstruction )
		//            throw new XPathException( "No argument allowed for " + ToString( type ) + "() test" );	// TODO: better description
		//    }

		//    // for optimizer use
		//    public NodeTypeTest( NodeTypeTest other, Axes axis )
		//        : base( axis )
		//    {
		//        type = other.type;
		//        _param = other._param;
		//    }

		//    public override String ToString()
		//    {
		//        String strType = ToString( type );
		//        if( type == XPathNodeType.ProcessingInstruction && _param != null )
		//            strType += "('" + _param + "')";
		//        else
		//            strType += "()";

		//        return _axis.ToString() + "::" + strType;
		//    }

		//    private static String ToString( XPathNodeType type )
		//    {
		//        switch( type )
		//        {
		//            case XPathNodeType.Comment:
		//                return "comment";
		//            case XPathNodeType.Text:
		//                return "text";
		//            case XPathNodeType.ProcessingInstruction:
		//                return "processing-instruction";
		//            case XPathNodeType.All:
		//            case XPathNodeType.Attribute:
		//            case XPathNodeType.Element:
		//            case XPathNodeType.Namespace:
		//                return "node";
		//            default:
		//                return "node-type [" + type.ToString() + "]";
		//        }
		//    }

		//    public override bool Match( NSResolver nsm, XPathNavigator nav )
		//    {
		//        XPathNodeType nodeType = nav.NodeType;
		//        switch( type )
		//        {
		//            case XPathNodeType.All:
		//                return true;

		//            case XPathNodeType.ProcessingInstruction:
		//                if( nodeType != XPathNodeType.ProcessingInstruction )
		//                    return false;
		//                if( _param != null && nav.Name != _param )
		//                    return false;
		//                return true;

		//            case XPathNodeType.Text:
		//                if( nodeType == XPathNodeType.SignificantWhitespace )
		//                    return true;
		//                goto default;
		//            default:
		//                return type == nodeType;
		//        }
		//    }

		//    public override void GetInfo( out string name, out string ns, out XPathNodeType nodetype, NSResolver nsm )
		//    {
		//        name = _param;
		//        ns = null;
		//        nodetype = type;
		//    }
		//}



		//public abstract class NodeTest : NodeSet
		//{
		//    protected AxisSpecifier _axis;
		//    public NodeTest( Axes axis )
		//    {
		//        _axis = new AxisSpecifier( axis );
		//    }
		//    public abstract bool Match( XmlNamespaceManager nsm, XPathNavigator nav );
		//    public AxisSpecifier Axis { get { return _axis; } }
		//    public override object Evaluate( BaseIterator iter )
		//    {
		//        SimpleIterator iterAxis = _axis.Evaluate( iter );
		//        return new AxisIterator( iterAxis, this );
		//    }

		//    public abstract void GetInfo( out string name, out string ns, out XPathNodeType nodetype, XmlNamespaceManager nsm );

		//    public override bool RequireSorting
		//    {
		//        get
		//        {
		//            switch( _axis.Axis )
		//            {
		//                //case Axes.Ancestor:
		//                //case Axes.AncestorOrSelf:
		//                //case Axes.Preceding:
		//                //case Axes.PrecedingSibling:
		//                case Axes.Namespace:
		//                    return true;
		//                default:
		//                    return false;
		//            }
		//        }

		//    }

		//    public override XPathNodeType EvaluatedNodeType
		//    {
		//        get { return _axis.NodeType; }
		//    }
		//}

		//public class NodeTypeTest : NodeTest
		//{
		//    public readonly XPathNodeType type;
		//    protected String _param;
		//    public NodeTypeTest( Axes axis )
		//        : base( axis )
		//    {
		//        this.type = _axis.NodeType;
		//    }
		//    public NodeTypeTest( Axes axis, XPathNodeType type )
		//        : base( axis )
		//    {
		//        this.type = type;
		//    }
		//    [MonoTODO( "Better description." )]
		//    public NodeTypeTest( Axes axis, XPathNodeType type, String param )
		//        : base( axis )
		//    {
		//        this.type = type;
		//        _param = param;
		//        if( param != null && type != XPathNodeType.ProcessingInstruction )
		//            throw new XPathException( "No argument allowed for " + ToString( type ) + "() test" );	// TODO: better description
		//    }

		//    public override String ToString()
		//    {
		//        String strType = ToString( type );
		//        if( type == XPathNodeType.ProcessingInstruction && _param != null )
		//            strType += "('" + _param + "')";
		//        else
		//            strType += "()";

		//        return _axis.ToString() + "::" + strType;
		//    }

		//    private static String ToString( XPathNodeType type )
		//    {
		//        switch( type )
		//        {
		//            case XPathNodeType.Comment:
		//                return "comment";
		//            case XPathNodeType.Text:
		//                return "text";
		//            case XPathNodeType.ProcessingInstruction:
		//                return "processing-instruction";
		//            case XPathNodeType.All:
		//            case XPathNodeType.Attribute:
		//            case XPathNodeType.Element:
		//            case XPathNodeType.Namespace:
		//                return "node";
		//            default:
		//                return "node-type [" + type.ToString() + "]";
		//        }
		//    }

		//    public override bool Match( XmlNamespaceManager nsm, XPathNavigator nav )
		//    {
		//        XPathNodeType nodeType = nav.NodeType;
		//        switch( type )
		//        {
		//            case XPathNodeType.All:
		//                return true;

		//            case XPathNodeType.ProcessingInstruction:
		//                if( nodeType != XPathNodeType.ProcessingInstruction )
		//                    return false;
		//                if( _param != null && nav.Name != _param )
		//                    return false;
		//                return true;

		//            default:
		//                return type == nodeType;
		//        }
		//    }

		//    public override void GetInfo( out string name, out string ns, out XPathNodeType nodetype, XmlNamespaceManager nsm )
		//    {
		//        name = _param;
		//        ns = null;
		//        nodetype = type;
		//    }
		//}
	}

}